package cn.attaching.security;

import java.nio.charset.Charset;
import java.util.zip.CRC32;

/**
 * 进行CRC加密
 * 高杰创建于 2017/01/19.
 */
public class CrcSecurity {
    /**
     * 获取CRC32算法校验的结果
     * @param src
     * @return
     */
    public static long getCrc32(String src){
        if(src == null || src.length() == 0){
            return 0;
        }
        return getCrc32(src.getBytes(Charset.forName("UTF-8")));
    }

    /**
     * 获取字节数组的CRC32校验
     * @param bytes
     * @return
     */
    public static long getCrc32(byte[] bytes){
        CRC32 crc = new CRC32();
        crc.update(bytes);
        return crc.getValue();
    }

    /**
     * 获取一个字符串的CRC16校验
     * @param src
     * @return
     */
    public static int getCrc16(String src){
        /*
         * 在sun.misc.CRC16，有一个神奇的没完全实现的代码，不知道Oracle是怎么想的
         */
        if(src == null || src.length() == 0){
            return 0;
        }
         return getCrc16(src.getBytes(Charset.forName("UTF-8")));
    }

    /**
     * 获取字节数组的CRC16校验
     * @param bytes
     * @return
     */
    public static int getCrc16(byte[] bytes){
        CRC16 crc16 = new CRC16();
        return crc16.getCrc(bytes);
    }


    public static class CRC16{
        private short[] crcTable = new short[256];
        private int gPloy = 0x1021; // 生成多项式

        public CRC16() {
            computeCrcTable();
        }
        private short getCrcOfByte(int aByte) {
            int value = aByte << 8;
            for (int count = 7; count >= 0; count--) {
                if ((value & 0x8000) != 0) { // 高第16位为1，可以按位异或
                    value = (value << 1) ^ gPloy;
                } else {
                    value = value << 1; // 首位为0，左移
                }
            }
            value = value & 0xFFFF; // 取低16位的值
            return (short)value;
        }
        /**
         * 生成0 - 255对应的CRC16校验码
         */
        private void computeCrcTable() {
            for (int i = 0; i < 256; i++) {
                crcTable[i] = getCrcOfByte(i);
            }
        }

        public int getCrc(byte[] data) {
            int crc = 0;
            for (byte aData : data) {
                crc = ((crc & 0xFF) << 8) ^ crcTable[(((crc & 0xFF00) >> 8) ^ aData) & 0xFF];
            }
            crc = crc & 0xFFFF;
            return crc;
        }
    }
}
